package model;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

import applet.energy.ImageCanvas;
 
/**
 * Configuration model, a data structure mapping of the file configuration.gz. <p/>
 * The file uses the following format: <p/>
 * 
 *	1	(configuration #1) <p/>
 *	19	0	19	(total # of molecules, #of water, #of hexanol) <br/>
 *	1	3	8	(molecule 1, "3" means it is hexanol, "8": # of units 
 * it contains) <p/>
 *   219.490697448788       -202.695881098413        118.249646683020
 * (unit 1's x, y,z coordinates, it's O) <br/>
 *   219.006285362574       -201.676601288017        119.127891295215
 * (unit 2's x, y,z coordinates, it's C) <br/>
 *   219.392326930925       -200.303966583729        118.546130282350
 * (unit 3's x, y,z coordinates, it's C) <br/>
 *   218.652838234487       -199.187693377882        119.306847442501
 * (unit 4's x, y,z coordinates, it's C) <br/>
 *   218.756601011723       -197.742736188668        118.784418492880
 * (unit 5's x, y,z coordinates, it's C) <br/>
 *   218.215844522345       -196.693279650748        119.773268023503
 * (unit 6's x, y,z coordinates, it's C) <br/>
 *   217.768844086508       -195.402466001363        119.062218986572
 * (unit 7's x, y,z coordinates, it's C) <br/>
 *   220.431059823846       -202.647227405394        118.169794183184
 * (unit 8's x, y,z coordinates, it's H) <p/>
 *	2	3	8	(molecule 2, "3" means it is hexanol, "8": # of units it contains) <p/>
 *   224.129640863629       -202.176022273739        121.480648444801 <br/>
 *   224.821124728087       -202.556374367570        122.673159388110 <br/>
 *   226.216717776555       -201.908668488135        122.606848477905 <br/>
 *   226.124351062549       -200.377127594980        122.738950267215 <br/>
 *   227.352473812188       -199.570968060272        122.276974817196 <br/>
 *   227.293675199233       -198.106458202625        122.749577821808 <br/>
 *   228.365634213285       -197.229490483277        122.076204603292 <br/>
 *   223.210714175052       -202.063003368197        121.669924613431 <br/>
 *     . <br/>
 *     . <br/>
 *     . <p/>
 *	5146	(configuration #5146) <p/>
 *	24	17	7	(it contains 24 molecules, 17 water, 7 hexanol) <br/>
 *	1	3	8	(molecule 1, "3" means it is hexanol, "8": # of units it contains) <p/>
 * -167.808707321236        407.507345403590       -635.214907422766 <br/>
 * -168.504971887689        407.010836545553       -634.068785872758 <br/>
 * -169.971911076372        406.734792859659       -634.447585688207 <br/>
 * -170.631457170543        408.047528047409       -634.909457181709 <br/>
 * -171.818198854116        407.851125773489       -635.871054978747 <br/>
 * -172.259220655228        409.194438352822       -636.481473757250 <br/>
 * -173.366055166295        408.933406986711       -637.519924522100 <br/>
 * -166.978384384422        407.829737607963       -634.899230234548 <p/>
 *	2	1	4	(molecule 2, "1" means it is water, "4": # of units it contains) <p/>
 * -161.551467139601        411.007077352696       -635.060373782798
 * (unit 1's x, y,z coordinates, it's O) <br/>
 * -160.619465092062        411.112472872443       -635.251412542755
 * (unit 2's x, y,z coordinates, it's H) <br/>
 * -161.899078776437        410.532607226208       -635.815540048602
 * (unit 3's x, y,z coordinates, it's H) <br/>
 * -161.476658116437        410.959831350198       -635.181499441938 <br/>
 * (unit 4's x, y,z coordinates, it's a special site which does not need <br/>
 * to be represented in the final image) <br/>
 *     . <br/>
 *     . <br/>
 *     . <br/>
 */
public class ConfigModel {

	/** a list of configuration element, each element consists of several molecules */
	private List<ConfigElem> elemList;
	
	/** 
	 * a list of lists of configuration element, it contains all the configuration
	 * data
	 */
	private List<List<ConfigElem>> storageList;
	
	/** group label of similarity */
	private static int label;
	
	/**
	 * Construct ConfigModel, this class allows generating objects only from file,
	 * so we declare the constructor as private in order to avoid codes generating
	 * objects from <code>new</code> method.
	 */
	private ConfigModel(){
		elemList = new ArrayList<ConfigElem>();
	}

	/**
	 * Parse configuration file and generate the configuration model.
	 * 
	 * @param reader the buffered reader accesses configuration file.
	 * 
	 * @return configuration model
	 * 
	 * @throws IOException if there is some IO exception occur
	 */
	public static ConfigModel parseConfigModel(BufferedReader reader) throws Exception {
		ConfigModel model = new ConfigModel();
		
		ConfigElem elem = null;		
		int count = 0;
		while(true){
			elem = ConfigElem.parseElement(reader);
			if(elem == null){
				break;
			}
			++count;

			model.elemList.add(elem);
			if(count%200 == 0){
				System.out.println("Have parsed " + count + " configs");
			}
		}
		System.out.println("Have totally parsed " + count + " configs");
		
		return model;
	}
	
	/**
	 * build the list of lists of configuration element, which contains all
	 * the configuration data
	 */
	private void buildStorageList(){
		if(storageList == null){
			storageList = new ArrayList<List<ConfigElem>>();
			
			int all = ImageCanvas.POINT_NUM*ImageCanvas.POINT_NUM;
			for(int i = 0; i < all; i++){
				storageList.add(new ArrayList<ConfigElem>());
			}
			
			int index, x, y;
			for(ConfigElem elem: elemList){
				x = elem.getWaterNum();
				y = elem.getHexanolNum();
				index = ImageCanvas.POINT_NUM*y + x;
				
				storageList.get(index).add(elem);
			}
		}
	}
	
	/**
	 * Get a configuration element list according to the number of molecules of
	 * water and hexanol.
	 * 
	 * @param water the number of water molecule
	 * @param hexanol the number of hexanol molecule
	 * 
	 * @return the list of configuration elements
	 */
	public List<ConfigElem> getElemList(int water, int hexanol){
		if(storageList == null){
			buildStorageList();
		}
		
		return storageList.get(ImageCanvas.POINT_NUM*hexanol + water);
	}

	/**
	 * Store the configuration model as a file in folder specified by <code>
	 * folderName</code>. <p/>
	 * 
	 * Since the file configuration.gz is too large, it takes a long each time
	 *  we load it to memory, and if we want find a specific content, we also 
	 * need a time-consuming process. So we split it to small pieces for convenient
	 * access to the part we concern, and store it to file system. <p/>
	 * 
	 * The file format and folder structure is like below:	<p/>
	 * 
	 * 1) folder structure:	<p/>
	 * |- data	<br/>
	 * |-- 0x1	<br/>
	 * |--- 0	<br/>
	 * |--- 1	<br/>
	 * |--- ...	<br/>
	 * |--- 6	<br/>
	 * |-- 0x2	<br/>
	 * |--- 0	<br/>
	 * |--- ...	<br/>
	 * |--- 11	<br/>
	 * |-- 0x3	<br/>
	 * |-- ...	<br/>
	 * |-- 40x40	<br/>
	 * |--- ...	<p/>
	 * 
	 * The "num1xnum2" means the folder stores the data related to num1 water 
	 * molecules and num2 hexanol ones, and files in num1xnum2 folder represent 
	 * <p/>
	 * 
	 * 2) a configuration element data like this:	<p/>
	 * 
	 * 8	(the number of atoms)	<br/>
	 * O -35.815895 399.96198 -669.3866	(element, x coordinate, y z)	<br/>
	 * C -35.25986 401.15182 -669.9523	<br/>
	 * C -35.84752 401.48053 -671.3373	<br/>
	 * C -35.96277 400.32187 -672.34515	<br/>
	 * C -36.4701 400.75546 -673.73303	<br/>
	 * C -36.642956 399.58148 -674.7146	<br/>
	 * C -37.03109 399.93735 -676.16174	<br/>
	 * H -35.187313 399.62866 -668.76465	<br/>
	 * 
	 * @param folderName the folder where to store the configuration data
	 * 
	 * @throws IOException if there is some IO exception when storing data
	 */
	public void store(String folderName) throws IOException {
		File folder = new File(folderName);
		folder.mkdirs();
		
		File dir = null;
		String dirName = null;
		List<ConfigElem> list = null;
		PrintWriter writer = null;
		
		System.out.println("\nStore processing...");
		
		buildStorageList();
		
		int size, all = ImageCanvas.POINT_NUM*ImageCanvas.POINT_NUM;
		for(int i = 0; i<all; i++){
			list = storageList.get(i);
			label = 0;
			
			if(list.size() == 0){
				System.out.println(i%ImageCanvas.POINT_NUM + "x" + i/ImageCanvas.POINT_NUM + " no molecule");
				continue;
			}
			
			dirName = i%ImageCanvas.POINT_NUM + "x" + i/ImageCanvas.POINT_NUM;
			
			dir = new File(folder, dirName);
			dir.mkdirs();
			
			size = list.size();
			
			// check similarity
			list.get(0).setLabel(label++);
			for(int j = 1; j<size; j++){
				ConfigElem ce = list.get(j);
				
				int k = 0;
				for( ; k<j; k++){
					if(ce.isSimilar(list.get(k))){
						ce.setLabel(list.get(k).getLabel());
						
						break;
					}
				}
				
				if(k == j){
					ce.setLabel(label++);
				}
			}
			
			File tempDir = null;
			for(int j = 0; j<label; j++){
				tempDir = new File(dir, String.valueOf(j));
				tempDir.mkdirs();
			}
			
			File file = null;
			String dirPath = dir.getAbsolutePath();
			for(int j = 0; j<size; j++){
				ConfigElem ce = list.get(j);
				
				tempDir = new File(dirPath + "/" + ce.getLabel());
				file = new File(tempDir, String.valueOf(tempDir.list().length));
				file.createNewFile();
				
				writer = new PrintWriter(file);
				list.get(j).store(writer);
				
				writer.close();
				writer = null;
			}
		}
		
		System.out.println("Store finished");
	}
}
